package mymyoux.io
{
	import flash.events.NetStatusEvent;
	import flash.net.NetConnection;
	import flash.net.ObjectEncoding;
	import flash.net.Responder;
	
	import mymyoux.core.SVar;
	import mymyoux.events.AMFPHPEvent;
	import mymyoux.events.MEventDispatcher;
	import mymyoux.io.AMFPHPClasses.Telegramme;

	/**
	 * AMFPHP permet l'exécution de fonctions PHP directement en ActionScript et dispatche le <code>return</code>.
	 * @author Mymyoux
	 * @version 2.0
	 */
	public class AMFPHP extends MEventDispatcher
	{
		/**
		 * Erreur renvoyée si le message n'a pas été envoyé.
		 */
		static public const SEND_FAILED:String="Client.Error.MessageSend";
		/**
		 * Erreur renvoyée si la méthode n'a pas été trouvée.
		 */
		static public const METHOD_NOT_FOUND:String="AMFPHP_INEXISTANT_METHOD";
		/**
		 * Erreur renvoyée si le serveur est introuvable.
		 */
		static public const RUNTIME_ERROR:String="AMFPHP_RUNTIME_ERROR";
		/**
		 * Erreur renvoyée si le message retourné par le serveur est incorrect.
		 */
		static public const INDALIDE_MSG:String="INVALID_AMF_MESSAGE";
		/**
		 * Netconnection permettant la liaision avec le gateway AMFPHP.
		 */
		protected static var netConnection:NetConnection;
		/**
		 * Classe PHP où chercher les fonctions à appeler partagée à toutes les instances de AMFPHP.
		 */
		protected static var source:String;
		/**
		 * Classe PHP où chercher les fonctions à appeler. Est prioritaire par rapport à source si elle existe.
		 */
		protected var localsource:String;
		/**
		 * Crée un nouvel objet AMFPHP
		 * @param destination URL du <code>gateway.php</code> qui configure le serveur AMFPHP.
		 * @param source Classe où se trouve les fonctions PHP à appeler. 
		 */
		public function AMFPHP(source:String="ServiceClass",destination:String="http://localhost/php/amfphp/gateway.php")
		{
			if(source!="ServiceClass" && source!=AMFPHP.source)
			{
				localsource=source;
			}
			else
			{
				localsource=null;
			}
			//TODO:à vérifier
			AMFPHP.destination=destination;
			if(netConnection==null)
			{
				AMFPHP.destination=destination;
				AMFPHP.source=source;
			}
		}
		/**
		 * URL du gateway PHP.
		 */
		public static function get destination():String
		{
			if(netConnection!=null)
			{
				return netConnection.uri;
			}else
			{
				return "http://localhost/php/amfphp/gateway.php";
			}
		}
		public static function set destination(value:String):void
		{
			netConnection=new NetConnection();
			netConnection.objectEncoding=ObjectEncoding.AMF0;
			netConnection.connect(value);
		}
		/**
		 * Exécute la fonction PHP <code>name</code> avec les arguments <code>args</code> et renvoie le résultat à <code>callback</code>.
		 * @param methodName Nom de la fonction à exécuter.
		 * @param callback Fonction ActionScript à appeler en cas de retour de PHP ou lors d'une erreur.
		 * @param args Arguments à transmettre à la fonction PHP.
		 */
		public function call(methodName:String,callback:Function=null,...args):void
		{
			if(callback!=null)
			{
					addEventListener(methodName,callback);
			} 
			var r:RemoteObj=new RemoteObj(netConnection,localsource!=null?localsource:source);
			//TODO:Retirer les écouteurs une fois fini
			r.addEventListener(AMFPHPEventLocal.RESULT,onResult);
			r.addEventListener(AMFPHPEventLocal.ERROR,onFault);
			r.call(methodName,args);
		}
		/**
		 * Fonction appelée lorsque PHP renvoie un résultat.
		 * @param result Contient les résultats et les différentes fonctions ActionScript à exécuter.
		 */
		protected function onResult ( result :AMFPHPEventLocal) : void {
			
			var telegramme:Telegramme=result.value as Telegramme;
			if(telegramme!=null)
			{
				if(telegramme.calls!=null)
				{
					var n:int=telegramme.calls.length;
					for(var i:int=0;i<n;i+=2)
					{
						//dispatchEvent(new AMFPHPEvent(telegramme.calls[i],telegramme.calls[i+1],false));
						SVar.put(telegramme.calls[i],telegramme.calls[i+1]);
					}
				}
				dispatchEvent(new AMFPHPEvent(result.methodName,telegramme.value,telegramme.errors.length>0));
				//TODO:tordu à corriger!
				var count:int;
				for( var p:int=0;i<listeners.length;i++)
				{
					if(listeners[p].type==result.methodName)
					{
						count++;
						//removeEventListener(listeners[p].type,listeners[p].listener,listeners[p].useCapture);
						//break;
					}
				}
				for( p=0;i<listeners.length;i++)
				{
					if(listeners[p].type==result.methodName)
					{
						if(count==1)
						{
							removeEventListener(listeners[p].type,listeners[p].listener,listeners[p].useCapture);
						}else
						{
							list=listeners.splice(p,1);
						}
						break;
					}
				}
			}
		}
 		/**
 		 * Fonction appelée lorsque PHP renvoie une erreur.
 		 * @param fault Contient l'erreur.
 		 */
		protected function onFault ( fault :AMFPHPEventLocal) : void {
			dispatchEvent(new AMFPHPEvent(fault.methodName,fault.value,true));
		}	
		
	}
}
import flash.net.NetConnection;
import flash.net.Responder;
import mymyoux.events.MEventDispatcherLocal;
import flash.events.Event;
import flash.events.NetStatusEvent;
import flash.events.SecurityErrorEvent;

internal class RemoteObj extends MEventDispatcherLocal{
	protected var nc:NetConnection;
	protected var baseName:String;
	protected var parameters:Array;
	protected var methodName:String;
	public function RemoteObj(nc:NetConnection,baseName:String="ServiceClass")
	{
		this.nc=nc;			
		this.nc.addEventListener(NetStatusEvent.NET_STATUS,_onNetStatus);
		this.nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR,_onSecurityError);
		this.baseName=baseName;
	}
	public function call(methodName:String,args:Array=null):void
	{
		this.methodName=methodName;
		var method:String = baseName+"."+methodName ;
		var responder:Responder = new Responder(_onResult, _onFault) ;
		var params:Array=new Array();
		params.push(method);
		params.push(responder);
		(nc.call).apply(nc,params.concat(args));
	}
	private function _onResult ( result : Object ) : void {
		dispatchEvent(new AMFPHPEventLocal(AMFPHPEventLocal.RESULT,methodName,result));
		destroy();
	}
 
	private function _onFault ( fault : Object ) : void {
		dispatchEvent(new AMFPHPEventLocal(AMFPHPEventLocal.ERROR,methodName,fault.code));
		destroy();
	}	
	private function _onNetStatus(event:NetStatusEvent):void
	{
		dispatchEvent(new AMFPHPEventLocal(AMFPHPEventLocal.ERROR,methodName,"netstatus"/*event.info*/));
		destroy();
	}
	private function _onSecurityError(event:SecurityErrorEvent):void
	{
		dispatchEvent(new AMFPHPEventLocal(AMFPHPEventLocal.ERROR,methodName,"securityerror"));
		destroy();
	}
}
internal class AMFPHPEventLocal extends Event
{
	static public const RESULT:String="result";
	static public const ERROR:String="error";
	private var _methodName:String;
	private var _value:*;
	public function AMFPHPEventLocal(type:String,methodName:String,value:*,bubbles:Boolean=false,cancelable:Boolean=false){
		super(type,bubbles,cancelable);
		_value=value;
		_methodName=methodName;
	}
	public function get methodName():String
	{
		return _methodName;
	}
	public function get value():*
	{
		return _value;
	}
	public override function toString():String{
		return "[AMFPHPEventLocal value=\""+_value+"\" methodName=\""+_methodName+"\" type=\""+type+"\"]";
	}
}
